package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.PointsStatisticsVO;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.enums.PointsRecordType;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.IPointsRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 学习积分记录，每个月底清零 服务实现类
 * </p>
 *
 * @author lmk
 * @since 2023-11-09
 */
@Service
@RequiredArgsConstructor
public class PointsRecordServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements IPointsRecordService {
    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    /**
     * 签到功能实现
     *
     * @return
     */
    @Override
    public SignResultVO addSianResult() {
        //获取登录用户
        Long userId = UserContext.getUser();
        //获取日期
        LocalDate now = LocalDate.now();
        //拼接
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX +
                userId + now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        int offset = now.getDayOfMonth() - 1;//获取签到
        //保存签到消息
        Boolean ex = redisTemplate.opsForValue().setBit(key, offset, true);
        if (BooleanUtils.isTrue(ex)) {
            throw new BizIllegalException("已经签到，不允许重复签到");
        }
        int signDays = countSignDays(key, now.getDayOfMonth());
        int ward = 0;
        switch (signDays) {
            case 7:
                ward = 10;
                break;
            case 14:
                ward = 20;
                break;
            case 28:
                ward = 50;
                break;
        }
        mqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(userId, ward)
        );
        SignResultVO signResultVO = new SignResultVO();
        signResultVO.setSignPoints(ward);
        signResultVO.setSignDays(signDays);
        return signResultVO;
    }

    /**
     * 新增积分
     *
     * @param userId
     * @param points
     * @param type
     */
    @Override
    public void addPointsRecord(Long userId, int points, PointsRecordType type) {
        //获取当前时间
        LocalDateTime now = LocalDateTime.now();
        int maxPoints = type.getMaxPoints();
        //判断当前积分有没有上限
        int realPoints = points;
        if (maxPoints > 0) {
            LocalDateTime start = DateUtils.getDayStartTime(now);
            LocalDateTime end = DateUtils.getDayEndTime(now);
            queryUserPointsRecord(userId, start, end, type);
            if (realPoints >= maxPoints) {
                return;
            }
            if (realPoints + points > maxPoints) {
                realPoints = maxPoints - realPoints;
            }
        }
        // 没有，直接保存积分记录
        PointsRecord p = new PointsRecord();
        p.setPoints(realPoints);
        p.setUserId(userId);
        p.setType(type);
        save(p);
        //更新总积分到Redis
        String key = RedisConstants.POINTS_BOARD_KEY_PREFIX + now.format(DateUtils.POINTS_BOARD_SUFFIX_FORMATTER);
        redisTemplate.opsForZSet().incrementScore(key, userId.toString(), realPoints);
    }



    /**
     * 查询积分
     *
     * @return
     */
    @Override
    public List<PointsStatisticsVO> queryMyPointsToday() {
        // 1.获取用户
        Long userId = UserContext.getUser();
        // 2.获取日期
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime begin = DateUtils.getDayStartTime(now);
        LocalDateTime end = DateUtils.getDayEndTime(now);
        // 3.构建查询条件
        QueryWrapper<PointsRecord> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .eq(PointsRecord::getUserId, userId)
                .between(PointsRecord::getCreateTime, begin, end);
        // 4.查询
        List<PointsRecord> list = getBaseMapper().queryUserPointsByDate(wrapper);
        if (CollUtils.isEmpty(list)) {
            return CollUtils.emptyList();
        }
        // 5.封装返回
        List<PointsStatisticsVO> vos = new ArrayList<>(list.size());
        for (PointsRecord p : list) {
            PointsStatisticsVO vo = new PointsStatisticsVO();
            vo.setType(p.getType().getDesc());
            vo.setMaxPoints(p.getType().getMaxPoints());
            vo.setPoints(p.getPoints());
            vos.add(vo);
        }
        return vos;
    }

    /**
     * 查询签到
     *
     * @return
     */
    @Override
    public Byte[] querySignRecords() {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.获取日期
        LocalDate now = LocalDate.now();
        int dayOfMonth = now.getDayOfMonth();
        // 3.拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX
                + userId
                + now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
        // 4.读取
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (CollUtils.isEmpty(result)) {
            return new Byte[0];
        }

        int num = result.get(0).intValue();   //23

        Byte[] arr = new Byte[dayOfMonth];
        int pos = dayOfMonth - 1;
        while (pos >= 0) {
            arr[pos--] = (byte) (num & 1);
            // 把数字右移一位，抛弃最后一个bit位，继续下一个bit位
            num >>>= 1;
        }
        return arr;
    }


    //查询今日获得的积分
    private int queryUserPointsRecord(Long userId, LocalDateTime start, LocalDateTime end, PointsRecordType type) {
        QueryWrapper<PointsRecord> wrapper = new QueryWrapper<>();
        //根据条件查询
        wrapper.lambda().eq(PointsRecord::getUserId, userId)
                .eq(type != null, PointsRecord::getType, type)
                .between(start != null && end != null, PointsRecord::getCreateTime, start, end);
        //调用mapper
        Integer points = getBaseMapper().queryUserPointsRecord(wrapper);
        //判断并返回
        return points == null ? 0 : points;
    }

    //获取签到天数
    private int countSignDays(String key, int dayOfMonth) {
        final List<Long> list = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))
                        .valueAt(0));
        if (CollUtils.isEmpty(list)) {
            return 0;
        }
        Long num = list.get(0);
        int count = 0;
        while ((num & 1) == 1) {
            count++;
            num >>>= 1;
        }
        return count;
    }


}